/// file system
/// only fat32 now
pub mod block;
pub mod fat32;
//mod inode;
mod stdio;
use crate::drivers::BLOCK_DEVICE;
use crate::mm::UserBuffer;
use alloc::sync::Arc;
use alloc::vec::Vec;
use fat32::inode::{Fat32INode, NodeType};
use spin::Mutex;
/// File trait
pub trait File: Send + Sync {
    /// If readable
    fn readable(&self) -> bool;
    /// If writable
    fn writable(&self) -> bool;
    /// Read file to `UserBuffer`
    fn read(&self, buf: UserBuffer) -> usize;
    /// Write `UserBuffer` to file
    fn write(&self, buf: UserBuffer) -> usize;
}

use alloc::{collections::vec_deque::VecDeque, vec};
use block::BlockManager;
use lazy_static::lazy_static;
pub use stdio::{Stdin, Stdout};

lazy_static! {
    /// 文件系统实例
    pub static ref FS: fat32::fs::Fat32FS = {
        let blk_manager = Arc::new(
            BlockManager {
            device: BLOCK_DEVICE.clone(),
            cache: Arc::new(Mutex::new(VecDeque::new())),
            cache_number: 16,
        });
        fat32::fs::Fat32FS::new(blk_manager.clone())
    };
}
pub struct CaseTester {
    pub(crate) cases: Vec<Fat32INode>,
    pub(crate) id: Mutex<usize>,
}

impl CaseTester {
    pub fn next(&self) -> Option<&Fat32INode> {
        let mut id = self.id.lock();
        loop {
            if let Some(inode) = self.cases.get(*id) {
                match inode.node_type {
                    NodeType::Dir => {
                        *id += 1;
                        continue;
                    }
                    NodeType::File => {
                        if inode.name == "BOOTFS" || inode.name == "SYSTEM~1" {
                            *id += 1;
                            continue;
                        }
                        *id += 1;
                        return Some(inode);
                    }
                }
            } else {
                return None;
            }
        }
    }
}
lazy_static! {
    pub static ref TESTER: CaseTester = {
        let nodes = FS.ls("/");
        println!("****APPS****");
        for node in &nodes {
            match node.node_type {
                NodeType::Dir => {
                    continue;
                }
                NodeType::File => {
                    println!("name: {}, size: {}bytes", node.name, node.size);
                }
            }
        }
        println!("************");
        CaseTester {
            cases: nodes,
            id: Mutex::new(0),
        }
    };
}

pub fn list_apps() {
    let nodes = FS.ls("/");
    println!("****APPS****");
    for node in &nodes {
        match node.node_type {
            NodeType::Dir => {
                continue;
            }
            NodeType::File => {
                println!("name: {}, size: {}kb", node.name, node.size / 1024);
            }
        }
    }
    println!("************");
}
